home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacGofer 0.22d / MacGofer Sources / mac_files.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-08  |  37.5 KB  |  1,662 lines  |  [TEXT/MPS ]

  1. /*****************************************************************************
  2.  
  3.   mac_files.c:  Copyright (c) Kevin Hammond 1993.   All rights reserved.
  4.   
  5.   This module contains Mac file-related code.
  6.  
  7.   These routines are preferred over standard C routines because:
  8.   
  9.       1)    They give better error diagnostics
  10.     2)    They're faster
  11.     3)    They give better control over the Mac file system
  12.     4)    Aliases etc. can be handled properly
  13.   
  14.   HFS support originally by Humayan Lari, UNC.
  15.  
  16. *****************************************************************************/
  17.  
  18.  
  19. #pragma segment Files
  20.  
  21. #include "mac.h"
  22. #include <fcntl.h>
  23. #include <Files.h>
  24. #include <Errors.h>
  25. #include <Folders.h>
  26.  
  27. OSErr hopen();
  28.  
  29. extern CursHandle watchcurs;                /* The watch cursor */
  30. extern short      systemVersion;        /* Which system we're running under */
  31. extern char      *safemalloc();
  32.  
  33. /*************************************************
  34.  
  35.     Local definitions.
  36.  
  37. *************************************************/
  38.  
  39. /* File types, used by SF Dialogs */
  40.  
  41. char        texttypelist[]         = GoferTextTypes;
  42. char        projtypelist[]         = GoferProjectTypes;
  43.  
  44. OSType        creator         = GoferCreatorType;
  45. OSType        texttype        = GoferTextType;
  46. OSType        projtype        = GoferProjectType;
  47. OSType        preftype        = GoferPrefsType;
  48.  
  49. /* Dialog handlers */
  50.  
  51. char     *getfile(), *askgetfile(), *putfile(), *opendialog(), 
  52.          *savedialog(), *deletedialog(); 
  53.  
  54. Boolean  revertdialog(), oktosavedialog();
  55. int      shouldsavedialog();
  56.  
  57.  
  58. SFReply Reply;         /* Replies from SF dialogs                     */
  59. FSSpec ReplySpec;     /* The SF reply record in FSSpec form                */
  60. Boolean ReplyExists;     /* Does the reply exist? (Valid only after a putfile call)    */
  61. OSErr  ioResult = noErr; /* Result code of last file handling function             */
  62. OSErr  err;         /* Error codes from OS functions                 */
  63.  
  64. Boolean fileIsLocked();     /* Is the file locked? */
  65. Boolean isVolLocked();     /* Is the volume locked? */
  66. Boolean isUserLocked();     /* Has the user locked the file? */
  67.  
  68. /*
  69.   Check for error and notify user if necessary.
  70. */
  71.  
  72. Boolean CheckError(char *file, short action, OSErr result)
  73. {
  74.   if (result != noErr)
  75.     {
  76.       char actionstr[256],
  77.            errorstr[256],
  78.            resultstr[256];
  79.       short errorIndex;
  80.  
  81.       /* Do *not* overwrite original error code, if any */
  82.       if (ioResult == noErr)
  83.         ioResult = result;
  84.  
  85.       /* Get action and result code strings */
  86.       getindstring(actionstr, Res_Action_Strings, action);
  87.       numtostring((long)result,resultstr);
  88.  
  89.       /* Get error explanation */
  90.       switch (result)
  91.         {
  92.       case fLckdErr:
  93.       case permErr:
  94.         errorIndex = StringFileLocked;
  95.         break;
  96.       case vLckdErr:
  97.       case wPrErr:
  98.         errorIndex = StringDiskLocked;
  99.         break;
  100.       case fBsyErr:
  101.       case opWrErr:
  102.         errorIndex = StringFileBusy;
  103.         break;
  104.       case ioErr:
  105.         errorIndex = StringFileError;
  106.         break;
  107.       case noMacDskErr:
  108.         errorIndex = StringDiskError;
  109.         break;
  110.       case fnfErr:
  111.         errorIndex = StringFileNotFound;
  112.         break;
  113.       case wrPermErr:
  114.         errorIndex = StringPermError;
  115.         break;
  116.       default:
  117.         errorIndex = StringUnknownError;
  118.         break;
  119.     }
  120.       getindstring(errorstr, Res_File_Error_Strings, errorIndex);
  121.  
  122.       /* Notify user */
  123.       paramtext(file,actionstr,errorstr,resultstr);
  124.       SetCursor(&qd.arrow);
  125.       (void) Alert(Res_File_Error_Alert, NIL);
  126.  
  127.       return(TRUE);
  128.     }
  129.   else
  130.       return(FALSE);
  131. }
  132.  
  133.  
  134.  
  135. /*
  136.   Used by LookForFileInProject to search for a file
  137. */
  138.  
  139. static CInfoPBRec gSearchPB;
  140. static Boolean gEndSearch;
  141. static char   *gSearchfile;
  142. static short  gSearchvolnum;
  143. static long   gSearchdirID;
  144. static FSSpec fsspec;
  145.  
  146. void HandleFileSearch(long theDirID)
  147. /* Recursively search all directories for a file with the given name */
  148. {
  149.   register short index = 1;  /* for ioFDirIndex */
  150.  
  151.   do {
  152.     /* Prepare to call PBGetCatInfo for the current item */
  153.     gSearchPB.dirInfo.ioFDirIndex = index;
  154.     gSearchPB.dirInfo.ioDrDirID = theDirID;
  155.  
  156.     /* Request info about the current item */
  157.     /* And return to the previous level if there's no such item */
  158.     if (PBGetCatInfo(&gSearchPB,false) != noErr)
  159.       return;
  160.  
  161.     if (gSearchPB.dirInfo.ioFlAttrib & ioDirMask)
  162.       /* If we get a folder, we need to recurse */
  163.       HandleFileSearch(gSearchPB.dirInfo.ioDrDirID);
  164.     else
  165.       /* If we get a file, stop the search if it's the one we want */
  166.       {
  167.     if (equalfiles(gSearchfile,p2cstr((char *)gSearchPB.hFileInfo.ioNamePtr)))
  168.       {
  169.             gEndSearch = true;
  170.         gSearchvolnum = gSearchPB.hFileInfo.ioVRefNum; 
  171.         gSearchdirID = gSearchPB.hFileInfo.ioFlParID;
  172.         
  173.         resolvealias(&gSearchfile,&gSearchvolnum,&gSearchdirID,TRUE);
  174.       }
  175.       }
  176.  
  177.     /* Increment index for PBGetCatInfo */
  178.     index ++;
  179.   } while (!gEndSearch);
  180. }
  181.  
  182. Boolean DoFileSearch(char **file,short *volnum,long *dirID)
  183. /* Tries to locate the given file by searching a folder and all its subfolders for a file with the given name */
  184. {
  185.   Str255 nameBuffer;
  186.  
  187.   /* Set up the unchanging fields of the PBGetCatInfo parameter block */
  188.   gSearchPB.dirInfo.ioCompletion = NULL;
  189.   gSearchPB.dirInfo.ioNamePtr = nameBuffer;
  190.   gSearchPB.dirInfo.ioVRefNum = *volnum;
  191.  
  192.   /* Do the search */
  193.   gEndSearch = false;
  194.   gSearchfile = *file;
  195.   gSearchvolnum = 0;
  196.   gSearchdirID = 0;
  197.   HandleFileSearch(*dirID);
  198.  
  199.   /* Let our caller know how things went */
  200.   if (gSearchvolnum != 0 && gSearchdirID != 0)
  201.     {
  202.       *volnum = gSearchvolnum;
  203.       *dirID =  gSearchdirID;
  204.       *file =   gSearchfile;
  205.       return(TRUE);
  206.     }
  207.   else
  208.     return(FALSE);
  209. }
  210.  
  211. /*
  212.   Look for a file belonging to a project, and complain if it can't be located within the given folder.
  213. */
  214.  
  215. Boolean LookForFileInProject(char **file,short *volnum,long *dirID)
  216. {
  217.   FInfo finfo;
  218.   OSErr result;
  219.   char resultstr[256];
  220.  
  221.   result = hgetfinfo(*file,*volnum,*dirID,&finfo);
  222.   if (result != noErr && !DoFileSearch(file,volnum,dirID))
  223.     {
  224.       numtostring((long)result,resultstr);
  225.       paramtext(*file,resultstr,"","");
  226.       SetCursor(&qd.arrow);
  227.       (void) Alert(Res_CantFindProjectFile_Alert, NIL);
  228.       return(FALSE);
  229.     }
  230.   else
  231.       return(TRUE);
  232. }
  233.  
  234.  
  235.  
  236. /* Open the input file */ 
  237.  
  238. Open_The_File()
  239. {
  240.   char *file = opendialog();
  241.   updatewindows();
  242.   doopenfile(file,ReplySpec.vRefNum,ReplySpec.parID);
  243. }
  244.  
  245.  
  246. /*
  247.   Open the named file.
  248. */
  249.  
  250.  
  251. int doopenfile(f,volnum,dirID)
  252. char *f;
  253. short volnum;
  254. long  dirID;
  255. {
  256.   int windex;
  257.   
  258.   resolvealias(&f,&volnum,&dirID,FALSE);
  259.  
  260.   ioResult = noErr;
  261.  
  262.   if(*f!='\0')
  263.     if((windex = findMyWindowName(f,volnum,dirID,FALSE)) != ILLEGAL_WINDOW)
  264.       {
  265.         openthewindow(windex);
  266.     return(windex);
  267.       }
  268.     else
  269.       {
  270.         windex = CreateNewWindow(Res_OpenWindow,f,FALSE,TRUE,AUTO_POSITION,FALSE);
  271.         VREFNUM(windex) = volnum;
  272.         DIRID(windex) = dirID;
  273.         return(readtext(WINDOW(windex),f,TRUE,TRUE));
  274.       }
  275.   else
  276.     return(ILLEGAL_WINDOW);
  277. }
  278.  
  279.  
  280. /*
  281.     Open a window, but don't display it, as when printing from
  282.     an Apple event.
  283. */
  284.  
  285. doopenforprint(f,volnum,dirID)
  286. char  *f;
  287. short volnum;
  288. long  dirID;
  289. {
  290.   int windex = CreateNewWindow(Res_OpenWindow,f,FALSE,FALSE,FALSE,FALSE);
  291.   VREFNUM(windex) = volnum;
  292.   DIRID(windex) = dirID;
  293.   if(readtext(WINDOW(windex),f,TRUE,FALSE))
  294.     return(windex);
  295.   else
  296.     return(ILLEGAL_WINDOW);
  297. }
  298.  
  299. /*
  300.   Revert a file from a saved copy.
  301.   Revert even if apparently unchanged -- this file may be shared!
  302. */
  303.  
  304. revert(windex)
  305. int windex;
  306. {
  307.   ioResult = noErr;
  308.  
  309.   if(revertdialog(FILENAME(windex)))
  310.     {
  311.       updatewindows();
  312.       TESetSelect(0,32767,TEHANDLE(windex));
  313.       TEDelete(TEHANDLE(windex));
  314.       (void) readtext(WINDOW(windex),FILENAME(windex),FALSE,TRUE);
  315.     }
  316. }
  317.  
  318.  
  319. /* Delete a file */ 
  320.  
  321. dodelete()
  322. {
  323.   char *file = deletedialog();
  324.   ioResult = noErr;
  325.  
  326.   if(*file != '\0')
  327.     (void) CheckError(file, ActionDelete,
  328.       hdelete(file,ReplySpec.vRefNum,ReplySpec.parID));
  329. }
  330.  
  331.  
  332.  
  333. /*
  334.   Read text from a file into a window.
  335. */
  336.  
  337.  
  338. int readtext(window,file,closeOnFail,showWin)
  339. WindowPtr window;
  340. char *file;
  341. Boolean closeOnFail, showWin;
  342. {
  343.   int windex = findMyWindow(window);
  344.   
  345.   if(!isLegalWindow(windex))
  346.     return(ILLEGAL_WINDOW);
  347.     
  348.   if(!doread(file,VREFNUM(windex),DIRID(windex),windex)  && closeOnFail)
  349.     {
  350.       CloseAWindow(windex);
  351.       return(ILLEGAL_WINDOW);
  352.     }
  353.   else
  354.     {
  355.       /* Do this even for partially read files */
  356.       AdjustScrollBars(windex);
  357.       ScrollToSelection(windex);
  358.       InvalRect(&(*TEHANDLE(windex))->viewRect);
  359.  
  360.       FLAGS(windex) &= ~WLOCKED;
  361.       if(fileIsLocked(FILENAME(windex),VREFNUM(windex),DIRID(windex),FALSE))
  362.         if (isUserLocked(FILENAME(windex),VREFNUM(windex),DIRID(windex)))
  363.            FLAGS(windex) |= WFLOCKED;
  364.     else
  365.            FLAGS(windex) |= WVLOCKED;
  366.  
  367.       if(showWin)
  368.         {
  369.           DrawControls(WINDOW(windex));
  370.           DrawGrowIcon(WINDOW(windex));
  371.           DrawGoferIcons(windex);
  372.           ShowWindow(WINDOW(windex));
  373.     }
  374.  
  375.       saved(windex);
  376.       MODTIME(windex) = getmodtime(FILENAME(windex),VREFNUM(windex),DIRID(windex));
  377.       return(windex);
  378.     }
  379. }
  380.  
  381. Handle ConvertTabs(teh,length,path)
  382. Handle teh;
  383. long length;
  384. short path;
  385. {
  386.   int i, j, oj;
  387.   int tabs=0, nl=0;
  388.   Handle nteh = NIL;
  389.   char ch;
  390.  
  391.   for(i=0;i < length; ++i)
  392.     if((char) *(*teh+i) == '\t')
  393.       ++tabs;
  394.  
  395.   if(tabs > 0)
  396.     {
  397.       nteh=NewHandle(length+tabs*8);
  398.       if(nteh==NIL)
  399.         {
  400.       if(path >= 0)
  401.         (void)FSClose(path);
  402.       AbortError("Memory ","Not enough memory to convert tabs in file");
  403.     }
  404.  
  405.       for(i=j=0;i<length;++i)
  406.         if((ch = (char) *(*teh+i)) == '\t')
  407.       for(oj=j;j < (oj + 8 - (oj - nl)%8);++j)
  408.         *(*nteh+j) = ' ';
  409.        
  410.     else if(ch == '\r' || ch == '\n')
  411.       {
  412.         *(*nteh+j++) = '\n';
  413.         nl = j;
  414.       }
  415.  
  416.     else
  417.       *(*nteh+j++) = *(*teh+i);
  418.  
  419.       SetHandleSize(nteh,j);
  420.       DisposeHandle(teh);
  421.       return(nteh);
  422.     }
  423.   else
  424.     return(teh);
  425. }
  426.  
  427.  
  428. /*
  429.     Read a file into a window.
  430. */
  431.  
  432. /* Was there an error? */
  433. #define CHECK_READ_ERROR(theResult)    CheckError(file, ActionOpen, theResult)
  434.  
  435. /* Uses CHECK_READ_ERROR to check for an error, and then closes the file and leaves the routine if necessary */
  436. #define HANDLE_READ_ERROR(theResult)    if (CHECK_READ_ERROR(theResult)) { if (path != 0) (void) FSClose(path); return(FALSE); }
  437.  
  438. doread(file,volnum,dirID,windex)
  439. char *file;
  440. short volnum;
  441. long dirID;
  442. int windex;
  443. {
  444.   short path = 0;
  445.   long length;
  446.   Handle textHandle;
  447.   long nread;
  448.   OSErr readResult;
  449.  
  450.   /* Open the file */
  451.   SetCursor(*watchcurs);
  452.   HANDLE_READ_ERROR( hopen(file,volnum,dirID,fsRdPerm,&path) );
  453.  
  454.   /* Get its length, and complain if it's too big */
  455.   HANDLE_READ_ERROR( GetEOF(path,&length) );
  456.   if(length > TE_REC_SIZE)
  457.     {
  458.       Complain(file,StringFileTooLarge);
  459.       (void) FSClose(path);
  460.       return(FALSE);
  461.     }
  462.  
  463.   if(MaxBlock() < length + 1024)
  464.     {
  465.       Error("Memory ","Not enough memory left to open file");
  466.       (void) FSClose(path);
  467.       return(FALSE);
  468.     }
  469.       
  470.  
  471.   /* Allocate a handle so we can read in the text */
  472.   textHandle = NewHandle(length);
  473.   if (textHandle == NIL)
  474.     HANDLE_READ_ERROR( MemError() );
  475.  
  476.   /* This seems much less reliable than the code below.  It doesn't
  477.      check for partial blocks read, it uses much more memory
  478.      (up to 32K rather than 1K maximum), and doesn't retry network
  479.      reads. It also definitely won't work when BigTextEdit is plugged in. KH
  480.   */
  481.   /* Read the text */
  482.   nread = length;
  483.   HLockHi(textHandle);
  484.   readResult = FSRead(path,&nread,(Ptr)(*textHandle));
  485.   HUnlock(textHandle);
  486.  
  487.   /* If something went wrong, dispose of our handle before notifying the user */
  488.   if (readResult != noErr)
  489.     {
  490.       DisposeHandle(textHandle);
  491.       HANDLE_READ_ERROR(readResult);
  492.     }
  493.     
  494.   textHandle = ConvertTabs(textHandle,length,path);
  495.  
  496.   /* Now plug the handle into the window's TE record and ask TE to recalculate line breaks */
  497.   DisposeHandle((**TEHANDLE(windex)).hText);
  498.   (**TEHANDLE(windex)).hText = textHandle;
  499.   TECalText(TEHANDLE(windex));
  500.   
  501.   /* We're done! */
  502.   HANDLE_READ_ERROR( FSClose(path) );
  503.   path = 0;
  504.  
  505.   GetGoferFileInfo(windex);
  506.       
  507.   return(TRUE);
  508. }
  509.  
  510.  
  511.  
  512. /*
  513.     Read a project file.
  514. */
  515.  
  516. /* Was there an error? */
  517. #define CHECK_READPROJ_ERROR(theResult)        CheckError(file, ActionOpenProj, theResult)
  518.  
  519. /* Cleans up after an error by closing the project file and clearing the project */
  520. #define CLEANUP_READPROJ            { if (path != 0) (void) FSClose(path); clearProject(); return (FALSE); }
  521.  
  522. /* Uses CHECK_READPROJ_ERROR to check for an error, and then cleans up and leaves the routine if necessary */
  523. #define HANDLE_READPROJ_ERROR(theResult)    if (CHECK_READPROJ_ERROR(theResult)) CLEANUP_READPROJ;
  524.  
  525. Boolean doreadproject(file,volnum,dirID)
  526. char *file;
  527. short volnum;
  528. long dirID;
  529. {
  530.   extern short projvol;
  531.   extern long projdirID;
  532.  
  533.   short path = 0;
  534.   long length;
  535.   char buff[256], *pbuff = buff;
  536.   long nread;
  537.  
  538.   short filevolnum;
  539.   long filedirID;
  540.   int preservemodtime = TRUE;
  541.  
  542.   extern char projfile[];
  543.  
  544.   /* Check the project filename */
  545.   ioResult = noErr;
  546.   if(*file=='\0')
  547.     return(FALSE);
  548.  
  549.   /* Open the project file */
  550.   SetCursor(*watchcurs);
  551.   ioResult = noErr;
  552.   if (CHECK_READPROJ_ERROR( hopen(file,volnum,dirID,fsRdPerm,&path) ))
  553.     return(FALSE);
  554.  
  555.   /* Find out how long it is */
  556.   if (CHECK_READPROJ_ERROR( GetEOF(path,&length) ))
  557.     {
  558.       (void) CHECK_READPROJ_ERROR( FSClose(path) );
  559.       return(FALSE);
  560.     }
  561.  
  562.   /* Clear project */
  563.   clearProject();
  564.  
  565.   /* Loop through the project file, reading filenames */
  566.   while(length > 0)
  567.     {
  568.       /* Read the length of the filename */
  569.       nread = 1;
  570.       HANDLE_READPROJ_ERROR( FSRead(path,&nread,(Ptr) &buff) );
  571.       nread = (long) buff[0];
  572.  
  573.       /* Read the filename itself */
  574.       HANDLE_READPROJ_ERROR( FSRead(path,&nread,(Ptr) &buff) );
  575.       buff[nread] = '\0';
  576.  
  577.       /* Locate the file */
  578.       filevolnum = volnum;
  579.       filedirID = dirID;
  580.       if (!LookForFileInProject(&pbuff,&filevolnum,&filedirID))
  581.         CLEANUP_READPROJ;
  582.  
  583.       /* Add the file to the project */
  584.       preservemodtime &= AddPItem(pbuff,filevolnum,filedirID,preservemodtime);
  585.       length -= nread+1;
  586.       if(pbuff != buff)
  587.         {
  588.           free(pbuff);
  589.       pbuff = buff;
  590.     }
  591.     }
  592.  
  593.   /* Close the project file */
  594.   HANDLE_READPROJ_ERROR( FSClose(path) );
  595.   path = 0;
  596.  
  597.   /* We're done! */
  598.   strcpy(projfile,file);
  599.   projvol = volnum;
  600.   projdirID = dirID;
  601.   windowtofront(worksheet);
  602.   readScripts(0);
  603.   printPrompt();
  604.   return(TRUE);
  605. }
  606.  
  607.  
  608. /*
  609.     Restore Preferences from the named file.
  610. */
  611.  
  612. /* Was there an error? */
  613. #define CHECK_READPREFS_ERROR(theResult)    CheckError(file, ActionRestorePrefs, theResult)
  614.  
  615. /* 
  616.     Uses CHECK_READPREFS_ERROR to check for an error, 
  617.     and then closes the file and leaves the routine if necessary.
  618.     It is NOT an error if the preferences file doesn't exist!
  619.     This is now handled outside dorestoreprefs.
  620.     
  621.     KH
  622. */
  623. #define HANDLE_READPREFS_ERROR(theResult)    \
  624.     if (CHECK_READPREFS_ERROR(theResult)) { if (path != 0) (void) FSClose(path); return(FALSE); }
  625.  
  626. Boolean dorestoreprefs(file,volnum,dirID,toggles)
  627. char *file;
  628. short volnum;
  629. long dirID;
  630. Boolean toggles;
  631. {
  632.   short path = 0;
  633.   long length;
  634.   Boolean readOK = TRUE;
  635.   char buff[256];
  636.   long nread;
  637.  
  638.   /* Check the prefs filename */
  639.   ioResult = noErr;
  640.   if(*file=='\0')
  641.     return(FALSE);
  642.  
  643.   /* Open our file */
  644.   HANDLE_READPREFS_ERROR( hopen(file,volnum,dirID,fsRdPerm,&path) );
  645.  
  646.   /* Find out how long it is */
  647.   SetCursor(*watchcurs);
  648.   HANDLE_READPREFS_ERROR( GetEOF(path,&length) );
  649.  
  650.   /* Loop through all the preference items */
  651.   while(length > 0)
  652.     {
  653.       /* Read item length */
  654.       nread = 1;
  655.       HANDLE_READPREFS_ERROR( FSRead(path,&nread,(Ptr) &buff) );
  656.       nread = (long) buff[0];
  657.  
  658.       /* Read item */
  659.       HANDLE_READPREFS_ERROR( FSRead(path,&nread,(Ptr) &buff) );
  660.       buff[nread] = '\0';
  661.       restorepref(buff,toggles);
  662.  
  663.       /* Decrement length */
  664.       length -= nread+1;
  665.     }
  666.  
  667.   /* We're done */
  668.   HANDLE_READPREFS_ERROR( FSClose(path) );
  669.   path = 0;
  670.   return(TRUE);
  671. }
  672.  
  673.  
  674.  
  675.  
  676. /*
  677.     Write a file.
  678.  
  679.         Modified by HSL  (6/15/92 and 7/16/92 US) for HFS and System 7
  680. */
  681.  
  682.  
  683. /* 
  684.    HANDLE_WRITE_ERROR uses CHECK_WRITE_ERROR to check for an error.
  685.    If an error has occurred, it then closes and deletes the file
  686.    before exiting the routine.
  687.    
  688.    The following variables are shared between handle_write_error(),
  689.    dowrite() and TEWriteFile().
  690.  */
  691.  
  692. static short   wrtemppath = 0,  wrtempvolnum = 0;    /* Temporary path and volume    */
  693. static char *  wrtempfile;                /* Temporary file name        */
  694. static long    wrtempdirID = 0L;            /* Temporary directory ID    */
  695.  
  696.  
  697. /* Check for write errors */
  698. #define CHECK_WRITE_ERROR(file,theResult)  \
  699.     CheckError(file, ActionSave, theResult)
  700.  
  701. /* Close a path if it's open */
  702. #define CLOSE_OPEN_PATH(path)    \
  703.     if(path != 0) { (void) FSClose(path); path = 0; }
  704.  
  705. /* Handle the error and return to our caller */
  706. #define HANDLE_WRITE_ERROR(res)  \
  707.     if(!handle_write_error(file,res)) return(FALSE)
  708.  
  709.  
  710. /*
  711.     Handle an error during writing.
  712.     If the file is open for writing, close it and delete it.
  713.     
  714.     Return an appropriate exit code (TRUE == no error).
  715. */
  716.  
  717. Boolean handle_write_error(file,theResult)
  718. char *file;
  719. int theResult;
  720. {
  721.   if (CHECK_WRITE_ERROR(file,theResult))
  722.     {
  723.       CLOSE_OPEN_PATH(wrtemppath);
  724.       (void) hdelete(wrtempfile,wrtempvolnum,wrtempdirID);
  725.       return(FALSE);                                \
  726.     }
  727.   return(TRUE);
  728. }
  729.  
  730.  
  731. /*
  732.     Set a file system spec.
  733. */
  734.  
  735. void setfsspec(char *file,short volnum,long dirID,FSSpec *spec)
  736. {
  737.   spec->vRefNum = volnum;
  738.   spec->parID = dirID;
  739.  
  740.   strcpy((char *)spec->name,file);
  741.   c2pstr((char *)spec->name);
  742. }
  743.  
  744.  
  745. /*
  746.     Write a TE character buffer of length "length" to the open file
  747.     whose path is given by "path".
  748.     
  749.     This routine corrects an error in Humayan's code,
  750.     where a buffer might not be unlocked if the write failed.
  751.     
  752.     KH
  753. */
  754.  
  755. OSErr TEWriteFile(path,count,teh)
  756. short path;
  757. long count;
  758. TEHandle teh;
  759. {
  760.   OSErr result;
  761.   CharsHandle chbuff = TEGetText(teh);
  762.   HLockHigh((Handle) chbuff);
  763.   
  764.   result = FSWrite(path,&count,(Ptr)*chbuff);
  765.   HUnlock((Handle) chbuff);
  766.   return(result);
  767. }
  768.  
  769.  
  770. /*
  771.     Checks whether a file is locked.
  772. */
  773.  
  774.  
  775. Boolean fileIsLocked(file,volnum,dirID,HandleErr)
  776. char *file;
  777. short volnum;
  778. long dirID;
  779. Boolean HandleErr;
  780. {
  781.   OSErr result;
  782.   short path = 0;
  783.   
  784.   if(isUserLocked(file,volnum,dirID))
  785.     result = fLckdErr;
  786.   else
  787.     result = hopen(file,volnum,dirID,fsWrPerm,&path);
  788.    
  789.   if ( result == vLckdErr || result == opWrErr || result == fLckdErr || 
  790.        result == wPrErr   || result == permErr || result == wrPermErr )
  791.      {
  792.        if(HandleErr)
  793.          CheckError( file, ActionSave, result );
  794.        return(TRUE);
  795.      }
  796.    else
  797.      {
  798.        CLOSE_OPEN_PATH(path);
  799.        return(isVolLocked(volnum,dirID));
  800.      }
  801. }
  802.  
  803.  
  804. /*
  805.    Does the specified file exist.
  806. */
  807.  
  808.  
  809. Boolean checkFileExists(file,volnum,dirID)
  810. char *file;
  811. short volnum;
  812. long dirID;
  813. {
  814.   OSErr ioresult;
  815.   short path = 0;
  816.   
  817.   ioresult = hopen(file,volnum,dirID,fsWrPerm,&path);
  818.   CLOSE_OPEN_PATH(path);
  819.    
  820.   return(ioresult != fnfErr);
  821. }
  822.  
  823.  
  824. Boolean isVolLocked(volnum,dirID)
  825. short volnum;
  826. long dirID;
  827. {
  828.   char *tempfile = "Gofer Temp ∂∂∂ ƒƒƒ 12345";
  829.   OSErr result = hcreate(tempfile,volnum,dirID,creator,'????');
  830.   hdelete(tempfile,volnum,dirID);
  831.   return( result == vLckdErr || result == opWrErr || result == wPrErr ||
  832.           result == permErr  || result == wrPermErr );
  833.  
  834. }
  835.  
  836.  
  837. /*
  838.     Write the contents of a window to "file".
  839.     fileExists indicates whether the file exists already.
  840. */
  841.  
  842.  
  843. Boolean dowrite(file,type,volnum,dirID,winfo,updatewinfo)
  844. char *file;
  845. OSType type;
  846. short volnum;
  847. long dirID;
  848. int winfo;
  849. Boolean updatewinfo;
  850. {
  851.   long count;
  852.   char  tempfile[256];
  853.   Boolean usingTempItemsFolder = FALSE;        /* Whether we're using the Sys7 temporary folder. */
  854.   Boolean fileExists;                /* Whether the file exists.                       */  
  855.  
  856.   /* Prepare to save */
  857.   SetCursor(*watchcurs);
  858.  
  859.   /* Set the temporary write path to NULL */
  860.   wrtemppath = 0;
  861.  
  862.   /*
  863.          Check whether we can write the original file.
  864.          This prevents us overwriting locked or shared files.
  865.  
  866.      It's not necessary to keep the file open, since nothing
  867.      can interrupt us while we're writing (this is because
  868.      System-7 is non-preemptive).  On the other hand, it should
  869.      be harmless to do so if we choose, and may prevent e.g.
  870.      network problems if the system ever becomes preemptive.
  871.      
  872.      If the file exists, then we write to a temporary file,
  873.      ideally in the Temporary Items folder.
  874.      
  875.      Otherwise we write the file directly.
  876.   */
  877.   
  878.   fileExists = checkFileExists(file,volnum,dirID);
  879.  
  880.   /* Unset the locked flags -- these will be set later */
  881.   if(updatewinfo)
  882.      FLAGS(winfo) &= ~WLOCKED;
  883.  
  884.   if(fileExists)
  885.     {
  886.       unsigned long seconds;
  887.       
  888.       /*
  889.          Make up a name for our temporary file.
  890.          We need to use strcpy() here!! KH
  891.       */
  892.  
  893.       strcpy(tempfile,GOFER_TEMP);
  894.       wrtempfile = tempfile;
  895.       
  896.       GetDateTime(&seconds);
  897.       numtostring((long)seconds,tempfile+sizeof(GOFER_TEMP)-1);
  898.  
  899.       /* Under System 7, try to put it in the temporary items folder */
  900.       usingTempItemsFolder =
  901.           systemVersion >= 0x0700   &&
  902.             FindFolder(volnum,kTemporaryFolderType,
  903.                kCreateFolder,&wrtempvolnum,&wrtempdirID) == noErr;
  904.  
  905.       /* Check the lock status */
  906.       if(fileIsLocked(file,volnum,dirID,TRUE) )
  907.         {
  908.       if(updatewinfo)
  909.             if(isUserLocked(file,volnum,dirID) )
  910.           FLAGS(winfo) |= WFLOCKED;
  911.         else
  912.           FLAGS(winfo) |= WVLOCKED;
  913.           return( FALSE );
  914.     }
  915.  
  916.     }
  917.   else
  918.     wrtempfile = file;
  919.     
  920.   /*
  921.      If we can't use the special folder, or we're writing the file itself,
  922.      then we use the real file's directory
  923.   */
  924.  
  925.   if (!usingTempItemsFolder)
  926.     {
  927.       wrtempvolnum = volnum;
  928.       wrtempdirID = dirID;
  929.     }
  930.   
  931.   /* Create our temporary or new file */
  932.   HANDLE_WRITE_ERROR( hcreate(wrtempfile,wrtempvolnum,wrtempdirID,creator,type) );
  933.  
  934.   /* Open it (NB: wrtemppath should be 0 here) */
  935.   HANDLE_WRITE_ERROR( hopen(wrtempfile,wrtempvolnum,wrtempdirID,fsWrPerm,&wrtemppath) );
  936.  
  937.   /* Allocate space for the text */
  938.   count = (*TEHANDLE(winfo))->teLength;
  939.   HANDLE_WRITE_ERROR( SetEOF(wrtemppath,count) );
  940.  
  941.   /* Write it */
  942.   HANDLE_WRITE_ERROR( TEWriteFile(wrtemppath,count,TEHANDLE(winfo)) );
  943.   
  944.   /* Close it */
  945.   HANDLE_WRITE_ERROR( FSClose(wrtemppath) );
  946.   wrtemppath = 0;
  947.  
  948.   /* Flush volume info */
  949.   HANDLE_WRITE_ERROR( FlushVol(NIL,wrtempvolnum) );
  950.   HANDLE_WRITE_ERROR( FlushVol(NIL,volnum) );
  951.  
  952.   /* If the file already existed, then we used a temporary file */
  953.   if(fileExists)
  954.     {
  955.       /* If possible, use Sys7 FSpExchangeFiles to swap the two files */
  956.       if(systemVersion >= 0x0700)
  957.         {
  958.           FSSpec tempspec, origspec;
  959.  
  960.           setfsspec(tempfile,wrtempvolnum,wrtempdirID,&tempspec);
  961.           setfsspec(file,volnum,dirID,&origspec);
  962.  
  963.           HANDLE_WRITE_ERROR( FSpExchangeFiles(&tempspec,&origspec) );
  964.       HANDLE_WRITE_ERROR( FSpDelete(&tempspec) );
  965.         }
  966.  
  967.       /*
  968.          If we can't swap the files, delete the original and rename the 
  969.      temporary file.  Note the assumption that the temporary file was
  970.      created in the same directory as the original file.
  971.       */
  972.       else
  973.         {
  974.           HANDLE_WRITE_ERROR( hdelete(file,volnum,dirID) );
  975.           HANDLE_WRITE_ERROR( hrename(wrtempfile,wrtempvolnum,wrtempdirID,file) );
  976.     }
  977.     }
  978.  
  979.   /* We're done! */
  980.   if(updatewinfo)
  981.     {
  982.       saved(winfo);
  983.       VREFNUM(winfo) = volnum;
  984.       DIRID(winfo) =   dirID;
  985.  
  986.       /* Set the file info */
  987.       SetGoferFileInfo(file,winfo);
  988.  
  989.       MODTIME(winfo) = getmodtime(file,volnum,dirID);
  990.      
  991.       /* Redraw the locked icon, in case it's changed */ 
  992.       DrawLockedIcon(winfo);
  993.     }
  994.   
  995.   return(TRUE);
  996. }
  997.  
  998.  
  999.  
  1000. /*
  1001.   Save a file.
  1002. */
  1003.  
  1004. Boolean dosave(winfo,type,dodialog,checksave,setname)
  1005. int winfo;
  1006. Boolean dodialog,checksave,setname;
  1007. OSType type;
  1008. {
  1009.   char savefile[256], oldfile[256];
  1010.   Boolean savedOK = FALSE;
  1011.   
  1012.   ioResult = noErr;
  1013.   if(checksave && SAVED(winfo))
  1014.     return(TRUE);
  1015.  
  1016.   strcpy(oldfile,FILENAME(winfo));
  1017.   
  1018.   if(dodialog)
  1019.     {
  1020.       short menuitem = findmenuitem(Menu_Window,3,1024,oldfile);
  1021.  
  1022.       if (strcmp(strcpy(savefile,savedialog(oldfile)),"") != 0)
  1023.         {
  1024.       updatewindows();
  1025.  
  1026.           savedOK = dowrite(savefile,type,
  1027.                         ReplySpec.vRefNum,ReplySpec.parID,winfo,setname);
  1028.     
  1029.       if(setname && savedOK)
  1030.         {
  1031.            Boolean doupdateold = findHighestVersion(oldfile) > 0;
  1032.               setitem(Menu_Window,menuitem,savefile);
  1033.               setwindowtitle(winfo,savefile);
  1034.               if(doupdateold)
  1035.             resetversions(oldfile,winfo);
  1036.         }
  1037.     }
  1038.     }
  1039.   else
  1040.     {
  1041.       if(MODTIME(winfo) == getmodtime(oldfile,VREFNUM(winfo),DIRID(winfo)) ||
  1042.          oktosavedialog(oldfile))
  1043.      {
  1044.        updatewindows();
  1045.            savedOK = dowrite(oldfile,type,VREFNUM(winfo),DIRID(winfo),winfo,setname);
  1046.      }
  1047.     }
  1048.  
  1049.   return(savedOK);
  1050. }      
  1051.  
  1052.  
  1053. /*
  1054.   Simple save, no dialog unless an untitled window.
  1055.   The worksheet cannot be saved this way.
  1056. */
  1057.   
  1058.  
  1059. save(winfo)
  1060. int winfo;
  1061. {
  1062.   dosave(winfo,texttype,VIRGIN(winfo),TRUE,TRUE);
  1063. }
  1064.  
  1065.  
  1066.  
  1067. /*
  1068.   Save as dialog.
  1069. */
  1070.  
  1071. Boolean saveas(winfo)
  1072. int winfo;
  1073. {
  1074.   return(dosave(winfo,texttype,TRUE,FALSE,TRUE));
  1075. }
  1076.  
  1077.  
  1078. /*
  1079.   Save a Copy: like "Save as" except the name is not changed
  1080.   within MacGofer.
  1081. */
  1082.   
  1083.  
  1084. saveacopy(winfo)
  1085. int winfo;
  1086. {
  1087.   dosave(winfo,texttype,TRUE,FALSE,FALSE);
  1088. }
  1089.  
  1090.  
  1091. /*
  1092.   Set the file info.
  1093. */
  1094.  
  1095. setfiletype(file,volume,dirID,creator,type)
  1096. Str255 file;
  1097. short volume;
  1098. long dirID;
  1099. char creator[];
  1100. char type[];
  1101. {
  1102.   FInfo    finderinfo;
  1103.  
  1104.   hgetfinfo(file,volume,dirID,&finderinfo);
  1105.   finderinfo.fdType = (OSType) type;
  1106.   finderinfo.fdCreator = (OSType) creator;
  1107.   hsetfinfo(file,volume,dirID,&finderinfo);
  1108. }
  1109.  
  1110.  
  1111. /*
  1112.   Create a new file of the specified type.
  1113. */
  1114.  
  1115. createfile(file,volnum,dirID,type)
  1116. char *file;
  1117. short volnum;
  1118. long dirID;
  1119. OSType type;
  1120. {
  1121.   OSErr result = hcreate(file,volnum,dirID,creator,type);
  1122.  
  1123.   /* If unsuccessful report the error using CheckError() */  
  1124.   if( result != noErr && result != dupFNErr)
  1125.     CheckError(file, ActionCreate, result);
  1126.   else
  1127.     setfiletype(file,volnum,dirID,creator,type);
  1128. }
  1129.  
  1130.  
  1131.  
  1132. /*
  1133.   Miscellaneous dialog handlers connected with file I/O.
  1134. */
  1135.  
  1136.  
  1137. char *savedialog(defaultsave)
  1138. char *defaultsave;
  1139. {
  1140.   return(putfile("Save Gofer Source as:",defaultsave,"Save"));
  1141. }
  1142.  
  1143.  
  1144. char *opendialog()
  1145. {
  1146.   return(getfile(1,texttypelist,"Open Gofer File","Open"));
  1147. }
  1148.  
  1149.  
  1150. char *deletedialog()
  1151. {
  1152.   return(askgetfile(-1,texttypelist,"Delete File","Delete",0,Res_Dlg_SFP_Delete));
  1153. }
  1154.  
  1155.  
  1156. Boolean revertdialog(f)
  1157. char *f;
  1158. {
  1159.   return(yesnodialog(Res_Dlg_Revert,f,"",CANCEL)==OK);
  1160. }
  1161.  
  1162.  
  1163. Boolean oktosavedialog(f)
  1164. char *f;
  1165. {
  1166.   return(yesnodialog(Res_Dlg_OkToSave,f,"",CANCEL)==OK);
  1167. }
  1168.  
  1169. int shouldsavedialog(w,s)
  1170. int w;
  1171. char *s;
  1172. {
  1173.   return(yesnodialog(Res_Dlg_Save,FILENAME(w),s,OK));
  1174. }
  1175.  
  1176.  
  1177.  
  1178. /*
  1179.     Save and Restore Information about a Gofer Text file.
  1180.     Gofer understands standard EFNT 1003 and MPSR 1005
  1181.     resources used by other editors as well as its own
  1182.     format.
  1183. */
  1184.  
  1185.  
  1186. #define    FILEINFOVERSION        2
  1187.  
  1188.  
  1189. /* MacGofer's own file information */
  1190. typedef struct
  1191.   {
  1192.     short infoversion;
  1193.     short literate, hasknum;
  1194.     short fontsize, fontnum;
  1195.     long  selStart, selEnd;
  1196.     Rect  portRect, iconportRect;
  1197.   }
  1198. GoferFileInfo;
  1199.  
  1200.  
  1201. /* Structure of an MPW File Info record -- MPSR 1005 */
  1202.  
  1203. typedef struct
  1204.   {
  1205.     short fontsize;
  1206.     char  fontname[32];
  1207.     short filler_6;
  1208.     short tabs;
  1209.     Rect  shrunk, grown;
  1210.     short filler_a89a;
  1211.     short style;
  1212.     long selStart, selEnd;
  1213.     int filler_0;
  1214.     short indent_showinvis;
  1215.   }
  1216. MPWFileInfo;
  1217.  
  1218.  
  1219. /* Structure of a Font Info record -- EFNT 1003 */
  1220. typedef struct
  1221. {
  1222.   short fontSize;
  1223.   Str255 fontName;
  1224. }  FileFontInfo;
  1225.  
  1226.  
  1227. /*
  1228.     Save information for a file.
  1229. */
  1230.  
  1231. SetGoferFileInfo(file,winfo)
  1232. char *file;
  1233. int winfo;
  1234. {
  1235.   OSErr err;
  1236.   GoferFileInfo *finfop;
  1237.   short path = HOpenResFile(VREFNUM(winfo),DIRID(winfo),c2pstr(file),fsWrPerm);
  1238.   Handle HFinfo = NewHandle(sizeof(GoferFileInfo));
  1239.   FileFontInfo  *efinfop;
  1240.   Handle HEFinfo = NewHandle(sizeof(FileFontInfo));
  1241.   MPWFileInfo *mfinfop;
  1242.   Handle HMPWFinfo = NIL;
  1243.   Point origin;
  1244.   GrafPtr SavePort;
  1245.  
  1246.   if(path < 0)
  1247.     {
  1248.       HCreateResFile(VREFNUM(winfo),DIRID(winfo),file);
  1249.       path = HOpenResFile(VREFNUM(winfo),DIRID(winfo),file,fsWrPerm);
  1250.     }
  1251.   
  1252.   p2cstr(file);
  1253.   
  1254.   if(path < 0)
  1255.     return;
  1256.  
  1257.   /* Read MPW info if available */    
  1258.   HMPWFinfo = GetResource(MPWFileInfoType,Res_MPWFileInfo);
  1259.  
  1260.   /* If no MPW info, set up a default set */    
  1261.   if(HMPWFinfo == NIL)
  1262.     {
  1263.       HMPWFinfo = NewHandle(sizeof(MPWFileInfo));
  1264.       HLock(HMPWFinfo);
  1265.       mfinfop = (MPWFileInfo *) *HMPWFinfo;
  1266.       mfinfop->filler_6 = 6;
  1267.       mfinfop->filler_0 = 0;
  1268.       mfinfop->filler_a89a = 0xa89a;
  1269.       mfinfop->tabs = 8;
  1270.       mfinfop->style = 0xca74;    /* Plain */
  1271.       mfinfop->indent_showinvis = 0x100; /* Auto-Indent, Don't show invis */
  1272.       mfinfop->fontsize = 9; strcpy(mfinfop->fontname,"monaco");
  1273.       mfinfop->selStart = mfinfop->selEnd = 0;
  1274.    }
  1275.   else
  1276.     {
  1277.       HLock(HMPWFinfo);
  1278.       mfinfop = (MPWFileInfo *) *HMPWFinfo;
  1279.     }
  1280.     
  1281.   HLock(HFinfo);
  1282.   finfop = (GoferFileInfo *) *HFinfo;
  1283.  
  1284.   finfop->infoversion = FILEINFOVERSION;
  1285.   finfop->literate = (FLAGS(winfo) & WLITERATE)?1:0;
  1286.   finfop->hasknum =  (FLAGS(winfo) & WHNUM)?1:0;
  1287.   finfop->portRect = WINDOW(winfo)->portRect;
  1288.   
  1289.   GetPort(&SavePort);
  1290.   SetPort(WINDOW(winfo));
  1291.   SetPt(&origin,0,0);
  1292.   LocalToGlobal(&origin);
  1293.   OffsetRect(&finfop->portRect,origin.h,origin.v);
  1294.   mfinfop->shrunk = mfinfop->grown = finfop->portRect;
  1295.  
  1296.   if(ICONWINDOW(winfo)!=NIL)
  1297.     {
  1298.       finfop->iconportRect = ICONWINDOW(winfo)->portRect;
  1299.   
  1300.       SetPort(ICONWINDOW(winfo));
  1301.       SetPt(&origin,0,0);
  1302.       LocalToGlobal(&origin);
  1303.       OffsetRect(&finfop->iconportRect,origin.h,origin.v);
  1304.     }
  1305.  
  1306.   SetPort(SavePort);
  1307.  
  1308.   if(isEditWindow(winfo))
  1309.     {
  1310.       TEHandle teh = TEHANDLE(winfo);
  1311.       HLock(HEFinfo);
  1312.       efinfop = (FileFontInfo *) *HEFinfo;
  1313.  
  1314.       mfinfop->fontsize = finfop->fontsize = efinfop->fontSize = (*teh)->txSize;
  1315.       finfop->fontnum =  (*teh)->txFont;
  1316.       mfinfop->selStart = finfop->selStart = (long) (*teh)->selStart;
  1317.       mfinfop->selEnd = finfop->selEnd =   (long) (*teh)->selEnd;
  1318.       
  1319.       GetFontName(finfop->fontnum,&(efinfop->fontName));
  1320.       getfontname(finfop->fontnum,mfinfop->fontname);
  1321.       HUnlock(HEFinfo);
  1322.       
  1323.       SetHandleSize(HEFinfo,sizeof(short)+1+*((char *)(efinfop->fontName)));
  1324.  
  1325.       /* Create EFNT 1003 */
  1326.       addresource(HEFinfo,FontInfoType,Res_FontInfo,"Font Size & Name");
  1327.       err=ResError();
  1328.      }
  1329.   else
  1330.     {
  1331.       finfop->fontsize = finfop->fontnum = 0;
  1332.       finfop->selStart = finfop->selEnd = 0;
  1333.     }
  1334.   
  1335.   HUnlock(HFinfo);  
  1336.   addresource(HFinfo,GoferFileInfoType,Res_GoferFileInfo,"Gofer Information");
  1337.   err=ResError();
  1338.  
  1339.   HUnlock(HMPWFinfo); 
  1340.   addresource(HMPWFinfo,MPWFileInfoType,Res_MPWFileInfo,"MPW Window Info");
  1341.   err=ResError();
  1342.   
  1343.   CloseResFile(path);
  1344.   
  1345.   DetachResource(HFinfo);
  1346.   DisposeHandle(HFinfo);
  1347.   DetachResource(HEFinfo);
  1348.   DisposeHandle(HEFinfo);
  1349.   DetachResource(HMPWFinfo);
  1350.   DisposeHandle(HMPWFinfo);
  1351. }
  1352.  
  1353.  
  1354. /*
  1355.     Restore information when reading a file.
  1356. */
  1357.  
  1358.  
  1359. GetGoferFileInfo(winfo)
  1360. int winfo;
  1361. {
  1362.   short path = HOpenResFile(VREFNUM(winfo),DIRID(winfo),c2pstr(FILENAME(winfo)),fsRdPerm);
  1363.   Handle gres = NIL, fres = NIL, mres = NIL;
  1364.   GoferFileInfo *finfop;
  1365.   FileFontInfo *efinfop;
  1366.   MPWFileInfo *mfinfop;
  1367.  
  1368.   p2cstr(FILENAME(winfo));
  1369.   if(path < 0)
  1370.     return;
  1371.  
  1372.   /* MPSR 1005 takes priority over EFNT 1003 and Gofer info */    
  1373.   mres = GetResource(MPWFileInfoType,Res_MPWFileInfo);
  1374.   
  1375.   /* Restore the font, size and selection range */
  1376.   if(mres != NIL && (isEditWindow(winfo) || iconic(winfo)))
  1377.     {
  1378.       short fontnum;
  1379.       HLock(mres);
  1380.       mfinfop = (MPWFileInfo *) *mres;
  1381.       GetFNum(mfinfop->fontname,&fontnum);
  1382.       if(fontnum != 0 && mfinfop->fontsize > 0 && mfinfop->fontsize < 256)
  1383.     updateFont(winfo,fontnum,mfinfop->fontsize);
  1384.  
  1385.       /* Restore the selection range */
  1386.       TESetSelect((short)mfinfop->selStart,(short)mfinfop->selEnd,TEHANDLE(winfo));
  1387.     }
  1388.      
  1389.  
  1390.   /* EFNT 1003 takes priority over Gofer-specific info */   
  1391.   if(mres == NIL)
  1392.     fres = GetResource(FontInfoType,Res_FontInfo);
  1393.  
  1394.   /* Restore the font and size */
  1395.   if(fres != NIL && (isEditWindow(winfo) || iconic(winfo)))
  1396.     {
  1397.       short fontnum;
  1398.       HLock(fres);
  1399.       efinfop = (FileFontInfo *) *fres;
  1400.       GetFNum(efinfop->fontName,&fontnum);
  1401.       if(fontnum != 0 && efinfop->fontSize > 0 && efinfop->fontSize < 256)
  1402.     updateFont(winfo,fontnum,efinfop->fontSize);
  1403.       HUnlock(fres);
  1404.       DetachResource(fres);
  1405.       DisposeHandle(fres);
  1406.     }
  1407.     
  1408.   
  1409.   gres = GetResource(GoferFileInfoType,Res_GoferFileInfo);
  1410.   if(gres != NIL)
  1411.     {
  1412.       HLock(gres);
  1413.       finfop = (GoferFileInfo *) *gres;
  1414.       if(finfop->literate==1)
  1415.         FLAGS(winfo) |= WLITERATE;
  1416.       else
  1417.         FLAGS(winfo) &= ~WLITERATE;
  1418.   
  1419.       if(finfop->hasknum==1)
  1420.         FLAGS(winfo) |= WHNUM;
  1421.       else
  1422.         FLAGS(winfo) &= ~WHNUM;
  1423.  
  1424.  
  1425.       if(mres == NIL && (isEditWindow(winfo) || iconic(winfo)))
  1426.         {
  1427.       char thefont[256];
  1428.       getfontname(finfop->fontnum,thefont);
  1429.  
  1430.       /*
  1431.          Check whether the saved font exists and is a sensible size,
  1432.          and if so, set it for winfo's TE record
  1433.       */
  1434.  
  1435.       if(fres == NIL && *thefont != '\0' && finfop->fontsize > 0 && finfop->fontsize < 256)
  1436.         updateFont(winfo,finfop->fontnum,finfop->fontsize);
  1437.  
  1438.       /* Restore the selection range */
  1439.       TESetSelect((short)finfop->selStart,(short)finfop->selEnd,TEHANDLE(winfo));
  1440.         }
  1441.  
  1442.  
  1443.       /* Restore the size and position of the window, if possible */
  1444.       if(mres != NIL || finfop->infoversion > 0)
  1445.         {
  1446.       Rect pr = mres == NIL? finfop->portRect:mfinfop->shrunk;
  1447.       
  1448.       /* Sanity checks for off-screen windows */
  1449.       if(pr.left >= qd.screenBits.bounds.right - 10 ||
  1450.          pr.left <= qd.screenBits.bounds.left + 10)
  1451.         {
  1452.           pr.right -= pr.left;
  1453.           pr.left = qd.screenBits.bounds.left + 10;
  1454.           pr.right += pr.left;
  1455.         }
  1456.  
  1457.       if(pr.right >= qd.screenBits.bounds.right - 10 || 
  1458.          pr.right <=qd.screenBits.bounds.left + 10)
  1459.         pr.right = qd.screenBits.bounds.right - 10;
  1460.       
  1461.       if(pr.top >= qd.screenBits.bounds.bottom - 10 ||
  1462.          pr.top <= qd.screenBits.bounds.top + 10)
  1463.         {
  1464.           pr.bottom -= pr.top;
  1465.           pr.top = qd.screenBits.bounds.top + 10;
  1466.           pr.bottom += pr.top;
  1467.         }
  1468.  
  1469.       if(pr.bottom >= qd.screenBits.bounds.bottom - 10 ||
  1470.          pr.bottom <= qd.screenBits.bounds.top + 10)
  1471.         pr.bottom = qd.screenBits.bounds.bottom - 10;
  1472.       
  1473.         MoveWindow(WINDOW(winfo), pr.left, pr.top, false);
  1474.         SizeWindow(WINDOW(winfo), pr.right - pr.left, pr.bottom - pr.top, false);
  1475.       ResizeTheWindow(WINDOW(winfo));
  1476.  
  1477.       /* Restore the icon window position if it's been saved */
  1478.           if(finfop->infoversion > 1)
  1479.             {
  1480.           CreateIconWindow(winfo,FALSE);
  1481.           pr = finfop->iconportRect;
  1482.           
  1483.           /* Sanity tests for icons -- 26 is a magic number */
  1484.           if(pr.left >= qd.screenBits.bounds.right - 26)
  1485.             {
  1486.           pr.right -= pr.left;
  1487.               pr.left = qd.screenBits.bounds.right - 26;
  1488.           pr.right += pr.left;
  1489.         }
  1490.  
  1491.           if(pr.left <= qd.screenBits.bounds.left + 26)
  1492.             {
  1493.           pr.right -= pr.left;
  1494.               pr.left = qd.screenBits.bounds.left + 26;
  1495.           pr.right += pr.left;
  1496.         }
  1497.         
  1498.           if(pr.top >= qd.screenBits.bounds.bottom - 26)
  1499.             {
  1500.           pr.bottom -= pr.top;
  1501.               pr.top = qd.screenBits.bounds.bottom - 26;
  1502.           pr.bottom += pr.top;
  1503.         }
  1504.  
  1505.           if(pr.top <= qd.screenBits.bounds.top + 26)
  1506.             {
  1507.           pr.bottom -= pr.top;
  1508.               pr.top = qd.screenBits.bounds.top + 26;
  1509.           pr.bottom += pr.top;
  1510.         }
  1511.         
  1512.             MoveWindow(ICONWINDOW(winfo), pr.left, pr.top, false);
  1513.         }
  1514.     }
  1515.  
  1516.       HUnlock(gres);
  1517.       HUnlock(mres);
  1518.  
  1519.       CloseResFile(path);
  1520.       DetachResource(gres);
  1521.       DisposeHandle(gres);
  1522.       DetachResource(mres);
  1523.       DisposeHandle(mres);
  1524.     }
  1525.   else
  1526.     CloseResFile(path);
  1527. }
  1528.  
  1529.  
  1530. /*
  1531.     Determine whether a file is literate by:
  1532.     
  1533.     1)    Checking the file information;
  1534.     2)    Reading the first character.
  1535. */
  1536.  
  1537. IsLiterateFile(volnum,dirID,file)
  1538. short volnum;
  1539. long dirID;
  1540. char *file;
  1541. {
  1542.   short path;
  1543.   Boolean result = FALSE;
  1544.   
  1545.   resolvealias(&file,&volnum,&dirID,FALSE);
  1546.   
  1547.   path = HOpenResFile(volnum,dirID,c2pstr(file),fsRdPerm);
  1548.   
  1549.   p2cstr(file);
  1550.   if(path >= 0)
  1551.     {
  1552.       Handle gres = GetResource(GoferFileInfoType,Res_GoferFileInfo);
  1553.  
  1554.       if(gres != NIL)
  1555.         {
  1556.           result = ((GoferFileInfo *) *gres)->literate==1;
  1557.           CloseResFile(path);
  1558.           DetachResource(gres);
  1559.           DisposeHandle(gres);
  1560.        }
  1561.       else
  1562.         CloseResFile(path);
  1563.     }
  1564.  
  1565.   /* Otherwise, check whether the first character is '>' */  
  1566.   if(!result)
  1567.     {
  1568.       long length;
  1569.       if(hopen(file,volnum,dirID,fsRdPerm,&path) == noErr)
  1570.         {
  1571.       if( GetEOF(path,&length) == noErr && length > 0)
  1572.         {
  1573.           char firstch;
  1574.           length = 1;
  1575.               if(FSRead(path,&length,(Ptr)(&firstch)) == noErr)
  1576.             result = firstch == '>';
  1577.         }
  1578.        (void) FSClose(path);
  1579.     }
  1580.     }
  1581.   return(result?1:0);
  1582. }
  1583.  
  1584.  
  1585. /*
  1586.     Determine whether Haskell Numbers should be used for this file,
  1587.     by reading the saved file information.
  1588. */
  1589.  
  1590.  
  1591. IsHaskNumFile(volnum,dirID,file)
  1592. short volnum;
  1593. long dirID;
  1594. char *file;
  1595. {
  1596.   short path;
  1597.   Boolean result = FALSE;
  1598.   
  1599.   resolvealias(&file,&volnum,&dirID,FALSE);
  1600.   
  1601.   path = HOpenResFile(volnum,dirID,c2pstr(file),fsRdPerm);
  1602.  
  1603.   p2cstr(file);
  1604.   if(path >= 0)
  1605.     {
  1606.       Handle gres = GetResource(GoferFileInfoType,Res_GoferFileInfo);
  1607.  
  1608.       if(gres != NIL)
  1609.         {
  1610.           result = ((GoferFileInfo *) *gres)->hasknum==1;
  1611.           CloseResFile(path);
  1612.           DetachResource(gres);
  1613.           DisposeHandle(gres);
  1614.        }
  1615.       else
  1616.         CloseResFile(path);
  1617.     }
  1618.   return(result?1:0);
  1619. }
  1620.  
  1621.  
  1622. /*
  1623.     Resolve a System 7 alias
  1624. */
  1625.  
  1626.  
  1627. static FSSpec fsspec;
  1628. resolvealias(file,volnum,dirID,copyname)
  1629. char    **file;
  1630. long    *dirID;
  1631. short   *volnum;
  1632. Boolean copyname;
  1633. {
  1634.   /* Resolve an alias, perhaps */
  1635.   if(systemVersion >= 0x0700)
  1636.      {
  1637.     Boolean wasFolder, wasAliased;
  1638.  
  1639.         fsmakefsspec(*file,*volnum,*dirID,&fsspec);
  1640.         ResolveAliasFile(&fsspec,TRUE,&wasFolder,&wasAliased);
  1641.     
  1642.     if(wasFolder)
  1643.       return;
  1644.  
  1645.         if(wasAliased)
  1646.       {
  1647.             /* Copy the resolved alias */
  1648.         if(copyname)
  1649.           {
  1650.             *file = safemalloc((int)(fsspec.name[0])+1);
  1651.             pstrcopy(fsspec.name,*file);
  1652.               }
  1653.         else
  1654.           *file = (char *)fsspec.name;
  1655.  
  1656.         p2cstr(*file);
  1657.         *volnum =  fsspec.vRefNum;
  1658.         *dirID =   fsspec.parID;
  1659.       }
  1660.     }
  1661. }
  1662.